package minidb.index;

import minidb.record.*;
import minidb.type.Constant;
import static minidb.type.Const.*;
import minidb.buffer.*;
public class bTreePage implements index{
	private Constant SearchKey;
	private RecordPage record;
	private bTreeRoot root;
	private bTreeLeafe leafe;
	private bTreeNode node;
	private boolean max = false;
	private int pageNum = 0;
	private java.util.ArrayList<Integer> visited = new java.util.ArrayList<Integer>();
	
	final int MARK = INT_SIZE * 5;
	final int PARENT = INT_SIZE*3;
	final int TOTAL = INT_SIZE * 4;
	final int LEAFE_MAX = 125;
	
	public bTreePage(int pageNum, RecordPage record, TableInfo table){
		Buffer buf = bufpool.pin(pageNum);
		root = new bTreeRoot(buf, table);
		bufpool.unpin(buf);
		this.pageNum = root.next();
		leafe = null;
		node = null;
		this.record = record;
	}

	public bTreePage(int pageNum){
		Buffer buf = bufpool.pin(pageNum);
		root = new bTreeRoot(buf);
		bufpool.unpin(buf);
		this.pageNum = root.next();
		leafe = null;
		node = null;
	}
	
	public String fieldName(){
		return root.fieldName();
	}
	public void rewind(){
		close();
		max = false;
		this.pageNum = root.next();
		visited.clear();
	}
	
	public boolean isLeafe(){
		Buffer buf = bufpool.pin(pageNum);
		boolean result = buf.getInt(MARK)==1;
		bufpool.unpin(buf);
		return result;
	}
	
	public Boolean isOnly(){
		return root.isOnly();
	}
	public void toFirst(){
		rewind();
		while(!isLeafe()){
			node = new bTreeNode(pageNum);
			pageNum = node.next().next();
		}
		leafe =new bTreeLeafe(pageNum, MARK+INT_SIZE);
	}
	
	public RID beforeFirst(Constant SearchKey) {
		rewind();
		Node n;
		this.SearchKey = SearchKey;
		while(!isLeafe()){
			node = new bTreeNode(pageNum);
			n = node.next();
			while(n!=null){
				if(SearchKey.compareTo(record.get(n.rid()).get(root.fieldName())) <= 0){
					pageNum = n.next();
					break;
				}
				n = node.next();
			}
			if(n==null){
				max = true;
				break;
			}
			visited.add(new Integer(node.pageNum()));
		}
		if(max){
			return null;
		}
		leafe = new bTreeLeafe(pageNum, MARK+INT_SIZE);
		RID r = leafe.next();
		while(r!=null){
			if(SearchKey.compareTo(record.get(r).get(root.fieldName())) == 0){
				leafe.prev();
				return r;
			}
			else if(this.SearchKey.compareTo(record.get(r).get(root.fieldName())) > 0){
				r = leafe.next();
			}
			else
				break;
		}
		leafe.prev();
		return null;
	}

	public void close() {
		if(leafe!=null){
			leafe.close();
		}
		leafe = null;
		node = null;
	}

	public void delete(Constant val, RID rid) {
		//System.out.println("start"+val.toString());
		RID r = beforeFirst(val);
		//System.out.println("second"+val.toString());
		int total;
		int parent;
		int Page;	
		r = leafe.next();
		if(!root.isOnly()){
			while(r!=null){
				if(r.equals(rid))
					break;
				r = next();
			}
		}
		if(r==null)
			throw new RuntimeException("NO SUCE VALUE: " + val.toString());
		//System.out.println(val.toString()+"  "+r.toString()+ "  "+ rid.toString());
		leafe.prev();
		total = leafe.delete();
		Page = leafe.pageNum();
		if(total == 0){
			parent = 0;
			if(!visited.isEmpty())
				parent = visited.remove(visited.size()-1);
			if(parent==0)
				return;
			Buffer buf = bufpool.pin(Page);
			int prePage = buf.getInt(PRE_PAGE);
			int nextPage = buf.getInt(NEXT_PAGE);
			if(prePage!=0){
				bufpool.unpin(buf);
				buf = bufpool.pin(prePage);
				buf.putInt(NEXT_PAGE, nextPage);
			}
			if(nextPage!=0){
				bufpool.unpin(buf);
				buf = bufpool.pin(nextPage);
				buf.putInt(PRE_PAGE, prePage);
			}
			bufpool.unpin(buf);
			dbFile.deallocPage(Page);
			node = new bTreeNode(parent);
			Node nod = null;
			while(true){
				while((node.next()).next()!=Page) ;
				node.prev();				
				if(nod!=null){
					total = node.replace(nod);
				}
				else
					total = node.delete();
				if(total>1){
					if(!node.hasNext()){
						node.toLast();
						node.prev();
						Node n = node.next();
						while(!visited.isEmpty()){
							parent = visited.remove(visited.size()-1);
							Page = node.pageNum();
							node = new bTreeNode(parent);
							while(node.next().next()!= Page)
								;
							node.prev();
							node.replace(n.rid());
							if(node.hasNext())
								break;
						}
					}
					break;
				}
				else if(total==1){
					if(nod==null){
						node.toLast();
						node.prev();
						nod = node.next();
					}
				}
				if(visited.isEmpty()){
					Page = node.next().next();
					dbFile.deallocPage(node.pageNum());
					root.next(Page);
					buf = bufpool.pin(root.pageNum());
					buf.putInt(INT_SIZE * 4, Page);
					bufpool.unpin(buf);
					break;
				}
				else{
					Page = node.pageNum();
					parent = visited.remove(visited.size()-1);
				}
				buf = bufpool.pin(node.pageNum);
				prePage = buf.getInt(PRE_PAGE);
				nextPage = buf.getInt(NEXT_PAGE);
				if(prePage!=0){
					bufpool.unpin(buf);
					buf = bufpool.pin(prePage);
					buf.putInt(NEXT_PAGE, nextPage);
				}
				if(nextPage!=0){
					bufpool.unpin(buf);
					buf = bufpool.pin(nextPage);
					buf.putInt(PRE_PAGE, prePage);
				}
				bufpool.unpin(buf);
				dbFile.deallocPage(node.pageNum());
				node = new bTreeNode(parent);
			}			
		}
		else if(!leafe.hasNext()){
			leafe.prev();
		    Page = leafe.pageNum();
			r = leafe.next();
			while(!visited.isEmpty()){
				parent = visited.remove(visited.size()-1);
				node = new bTreeNode(parent);
				Node n = node.next();
				while(n!=null){
					if(n.next()==Page)
						break;
					n = node.next();
				}
				node.prev();
				node.replace(r);
				if(node.hasNext())
					break;
				Page = parent;
			}			
		}
		rewind();
	}

	public RID getRid() {
		if(max){
			if(node!=null){
				node.toLast();
				node.prev();
				return node.next().rid();
			}
			else{
				leafe.toLast();
				leafe.prev();
				return leafe.next();
			}
		}
		else{
			leafe.prev();
			return leafe.next();
		}
	}

	public void insert(Constant val, RID rid) {
		RID r = beforeFirst(val);
		Node n;
		if(val.isNull() && root.isOnly())
			throw new RuntimeException("VALUE CAN NOT BE NULL IN FIELD: "+this.fieldName());
		if(r!=null){
			if(root.isOnly())
				throw new RuntimeException("VALUE HAS ALREADY EXIST: " + val.toString());
		}
		if(max){
			while(!isLeafe()){
				visited.add(new Integer(node.pageNum()));
				node.toLast();
				node.prev();
				pageNum = node.next().next();
				node.prev();
				node.replace(rid);
				node = new bTreeNode(pageNum);
			}
			if(pageNum == 0)
				pageNum = root.next();
			leafe = new bTreeLeafe(pageNum, MARK+INT_SIZE);
			leafe.toLast();
			n = leafe.insert(rid);
		}
		else{
		//leafe.prev();
			n = leafe.insert(rid);
		}	
		while(n!=null){
			int ppageNum = 0;
			if(!visited.isEmpty())
				ppageNum = visited.remove(visited.size()-1);
			if(ppageNum == 0){
				node = null;
			}
			else{
				node = new bTreeNode(ppageNum);
			}
			if(node == null){
				int newPage = dbFile.allocPage();
				Buffer buf = bufpool.pin(newPage);
				buf.putInt(PARENT, 0);
				buf.putInt(NEXT_PAGE, 0);
				buf.putInt(PRE_PAGE, 0);
				buf.putInt(MARK, 0);
				buf.putInt(CURRENT_PAGE, newPage);
				buf.putInt(TOTAL, 0);
				bufpool.unpin(buf);
				node = new bTreeNode(newPage);
				node.insert(n);
				pageNum = root.next();
				root.next(newPage);
				buf = bufpool.pin(root.pageNum());
				buf.putInt(INT_SIZE * 4, newPage);
				bufpool.unpin(buf);
				buf = bufpool.pin(n.next());
				buf.putInt(PARENT, newPage);
				bufpool.unpin(buf);
				buf = bufpool.pin(pageNum);
				buf.putInt(PARENT, newPage);
				bufpool.unpin(buf);
				if(isLeafe()){
					leafe.Rid(new RID(pageNum,MARK+INT_SIZE));
					leafe.toLast();
					leafe.prev();
					RID a = leafe.next();
					n = new Node(a.pageNum(), a.rid(), pageNum);
				}
				else{
					int p =  INT_SIZE * 129;
					n = new Node(buf.getInt(p), buf.getInt(p+INT_SIZE), pageNum);
				}
				node.Rid(new RID(newPage, node.pos()));
				node.insert(n);
				rewind();
				return;
			}
			else{
				Node tmp = node.next();
				while(tmp!=null){
					if(tmp.next()==pageNum)
						break;
					tmp = node.next();
				}
				node.prev();
				n = node.insert(n);
				pageNum = ppageNum;
			}
		}
		rewind();
	}

	public boolean hasNext() {
		if(leafe != null){
			return leafe.hasNext();
		}
		else if(node != null)
			return node.hasNext();
		else
			return false;
	}
	
	public RID next(){
		if(leafe == null)
			return null;
		if(leafe.hasNext())
			return leafe.next();
		if(!leafe.nextPage())
			return null;
		return leafe.next();
	}
	
	public void DropPage(){
		pageNum = root.next();
		int tmp = 0;
		Buffer buf;
		while(!isLeafe()){
			node = new bTreeNode(pageNum);
			buf = bufpool.pin(pageNum);
			pageNum = node.next().next();
			tmp = buf.getInt(NEXT_PAGE);
			dbFile.deallocPage(buf.pageNum());
			while(tmp != 0){
				bufpool.unpin(buf);
				buf = bufpool.pin(tmp);
				tmp = buf.getInt(NEXT_PAGE);
				dbFile.deallocPage(buf.pageNum());
			}
			bufpool.unpin(buf);
		}
		buf = bufpool.pin(pageNum);
		tmp = buf.getInt(NEXT_PAGE);
		dbFile.deallocPage(buf.pageNum());
		while(tmp != 0){
			bufpool.unpin(buf);
			buf = bufpool.pin(tmp);
			tmp = buf.getInt(NEXT_PAGE);
			dbFile.deallocPage(buf.pageNum());
		}
		bufpool.unpin(buf);
	}
}
